"""
Command execution and logging for LogRun Manager.
"""

import os
import shutil
import subprocess
import time
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple

from .models import LogEntry
from .storage import LogStorage


class CommandRunner:
    """Executes commands with automatic logging."""
    
    def __init__(self, log_directory: Optional[str] = None, storage: Optional[LogStorage] = None):
        """Initialize command runner."""
        if log_directory is None:
            # Use XDG cache directory standard: ~/.cache/logrun
            cache_dir = Path.home() / ".cache" / "logrun"
            self.log_directory = cache_dir
        else:
            self.log_directory = Path(log_directory)
        
        self.log_directory.mkdir(parents=True, exist_ok=True)
        self.storage = storage or LogStorage()
    
    def _generate_log_filename(self, command: str) -> str:
        """Generate a log filename based on command and timestamp."""
        # Extract base command and subcommand
        parts = command.split()
        
        # Handle first part (extract filename from path)
        if parts:
            first_part = parts[0]
            # If it's a path, extract just the filename
            if '/' in first_part:
                first_part = first_part.split('/')[-1]
            # Remove file extensions for cleaner names
            if '.' in first_part and not first_part.startswith('.'):
                first_part = first_part.split('.')[0]
        else:
            first_part = "command"
        
        # Handle second part if exists
        if len(parts) >= 2:
            second_part = parts[1]
            # Also handle paths in second part
            if '/' in second_part:
                second_part = second_part.split('/')[-1]
            if '.' in second_part and not second_part.startswith('.'):
                second_part = second_part.split('.')[0]
            name = f"{first_part}_{second_part}"
        else:
            name = first_part
        
        # Clean filename - allow alphanumeric, dots, underscores, and hyphens
        name = "".join(c for c in name if c.isalnum() or c in "._-")
        
        # Add timestamp
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        return f"{name}-{timestamp}.log"
    
    def run_command(self, command: List[str], tags: Optional[List[str]] = None, live_mode: bool = False) -> Tuple[int, Optional[LogEntry]]:
        """
        Run a command with logging.
        
        Args:
            command: Command to execute as list of strings
            tags: Optional tags for the log entry
            live_mode: If True, show real-time output while logging
            
        Returns:
            Tuple of (exit_code, log_entry or None when execution is skipped)
        """
        if not command:
            print("❌ No command provided")
            return 1, None

        env_overrides, executable = self._extract_execution_context(command)
        command_env = os.environ.copy()
        command_env.update(env_overrides)

        if executable is None:
            print("❌ No executable specified after environment assignments")
            return 1, None

        if not self._command_exists(executable, command_env):
            print(f"❌ Command not found: {executable}")
            print("ℹ️  Command was not executed; no log entry created")
            return 127, None

        if not live_mode and not self._check_script_command():
            print("❌ Error: 'script' command not found. Please install util-linux package.")
            print("ℹ️  Command was not executed; no log entry created")
            return 127, None

        command_str = " ".join(command)
        log_filename = self._generate_log_filename(command_str)
        log_path = self.log_directory / log_filename
        
        print(f"🚀 Starting logged session: {command_str}")
        print(f"📝 Log file: {log_path}")
        print(f"⏰ Started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        print("")
        
        # Add entry to storage
        entry = self.storage.add_entry(
            command=command_str,
            log_file=str(log_path),
            tags=tags or []
        )
        
        start_time = time.time()
        exit_code = 0
        
        try:
            if live_mode:
                # Live mode: run command directly with real-time output while logging to file
                exit_code = self._run_command_live(command_str, log_path)
            else:
                # Traditional mode: use script command for full logging
                result = subprocess.run(
                    ['script', '-c', command_str, str(log_path)],
                    text=True,
                    env=command_env
                )
                exit_code = result.returncode
                
        except KeyboardInterrupt:
            print("\n⚠️  Command interrupted by user")
            exit_code = 130
        except Exception as e:
            print(f"❌ Error running command: {e}")
            exit_code = 1
        
        end_time = time.time()
        duration = end_time - start_time
        
        print("")
        print(f"⏰ Finished at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        if exit_code == 0:
            print("✅ Command completed successfully")
        else:
            print(f"❌ Command failed with exit code: {exit_code}")
        print(f"📝 Full log saved to: {log_path}")
        print(f"⏱️  Duration: {duration:.2f} seconds")
        
        # Update entry with completion info
        self.storage.update_entry(
            entry.id,
            end_time=datetime.now(),
            exit_code=exit_code,
            duration=duration
        )
        
        return exit_code, entry

    def _extract_execution_context(self, command: List[str]) -> Tuple[Dict[str, str], Optional[str]]:
        """Split leading environment assignments from the command."""
        env_overrides: Dict[str, str] = {}
        executable: Optional[str] = None

        for token in command:
            if executable is None and self._is_env_assignment(token):
                key, value = token.split('=', 1)
                env_overrides[key] = value
                continue

            executable = token
            break

        return env_overrides, executable

    def _is_env_assignment(self, token: str) -> bool:
        """Check if a token looks like an environment variable assignment."""
        if '=' not in token or token.startswith('-'):
            return False

        key, _ = token.split('=', 1)
        if not key or '/' in key:
            return False

        first_char = key[0]
        if not (first_char.isalpha() or first_char == '_'):
            return False

        if not all(ch.isalnum() or ch == '_' for ch in key):
            return False

        return True

    def _command_exists(self, executable: str, env: Dict[str, str]) -> bool:
        """Determine whether the target executable can be resolved."""
        candidate = executable
        candidate = os.path.expanduser(candidate)

        if os.path.isabs(candidate) or candidate.startswith('./') or candidate.startswith('../') or '/' in candidate:
            path_candidate = Path(candidate)
            return path_candidate.is_file() and os.access(path_candidate, os.X_OK)

        return shutil.which(candidate, path=env.get('PATH')) is not None
    
    def _check_script_command(self) -> bool:
        """Check if script command is available."""
        try:
            subprocess.run(['script', '--version'], 
                         capture_output=True, check=False)
            return True
        except FileNotFoundError:
            return False
    
    def _run_command_live(self, command_str: str, log_path: Path) -> int:
        """
        Run command with real-time output display while logging to file.
        
        Uses tee-like functionality to show output in real-time while logging.
        
        Args:
            command_str: Command string to execute
            log_path: Path to log file
            
        Returns:
            Exit code of the command
        """
        # Write initial info to log
        with open(log_path, 'w') as log_file:
            log_file.write(f"Command: {command_str}\n")
            log_file.write(f"Started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            log_file.write("=" * 80 + "\n\n")
        
        # Start the process
        process = subprocess.Popen(
            command_str,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True,
            bufsize=1,
            universal_newlines=True
        )
        
        # Read output line by line, display and log simultaneously
        try:
            with open(log_path, 'a') as log_file:
                for line in iter(process.stdout.readline, ''):
                    # Display in real-time
                    print(line, end='', flush=True)
                    # Log to file
                    log_file.write(line)
                    log_file.flush()
            
            # Wait for process completion
            process.wait()
            
            # Write completion info to log
            with open(log_path, 'a') as log_file:
                log_file.write(f"\n" + "=" * 80 + "\n")
                log_file.write(f"Finished at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                log_file.write(f"Exit code: {process.returncode}\n")
            
            return process.returncode
            
        except KeyboardInterrupt:
            process.terminate()
            try:
                process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                process.kill()
                process.wait()
            raise
